{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "85bee32a",
   "metadata": {},
   "source": [
    "Python内置的property装饰器就是负责把一个方法变成属性调用："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "5623bb48",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Student(object):\n",
    "    \n",
    "    @property\n",
    "    def score(self):\n",
    "        return self._score\n",
    "    \n",
    "    @score.setter\n",
    "    def score(self,value):\n",
    "        if not isinstance(value, int):\n",
    "            raise ValueError('score must be interger!')\n",
    "        if value < 0 or value > 100:\n",
    "            raise ValueError('score must between 0-100!')\n",
    "        self._score = value"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2cd919d4",
   "metadata": {},
   "source": [
    "property的实现比较复杂，先考察如何使用，把一个getter方法变成属性，只需要加上property就可以。\n",
    "此时，property本身又创建了另一个装饰器score.setter，负责把一个setter方法变成属性赋值。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "6c927061",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "60"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s = Student()\n",
    "s.score = 60\n",
    "s.score"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "3d9ce08d",
   "metadata": {},
   "outputs": [
    {
     "ename": "ValueError",
     "evalue": "score must between 0-100!",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mValueError\u001b[0m                                Traceback (most recent call last)",
      "\u001b[1;32m~\\AppData\\Local\\Temp/ipykernel_31892/3911184021.py\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0ms\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mscore\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;36m999\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;32m~\\AppData\\Local\\Temp/ipykernel_31892/1315596691.py\u001b[0m in \u001b[0;36mscore\u001b[1;34m(self, value)\u001b[0m\n\u001b[0;32m     10\u001b[0m             \u001b[1;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'score must be interger!'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     11\u001b[0m         \u001b[1;32mif\u001b[0m \u001b[0mvalue\u001b[0m \u001b[1;33m<\u001b[0m \u001b[1;36m0\u001b[0m \u001b[1;32mor\u001b[0m \u001b[0mvalue\u001b[0m \u001b[1;33m>\u001b[0m \u001b[1;36m100\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 12\u001b[1;33m             \u001b[1;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'score must between 0-100!'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m     13\u001b[0m         \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_score\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mvalue\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mValueError\u001b[0m: score must between 0-100!"
     ]
    }
   ],
   "source": [
    "s.score = 999"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8b4f1cae",
   "metadata": {},
   "source": [
    "当又property，我们在对实力属性操作的时候，就知道该属性很可能不是直接暴露的，而是通过getter和setter方法来实现。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b357d47b",
   "metadata": {},
   "source": [
    "还可以只定义只读属性，之定义getter方法，不定义setter方法就是一个只读属性："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "4e10b493",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Student(object):\n",
    "    \n",
    "    @property\n",
    "    def birth(self):\n",
    "        return self._birth\n",
    "    \n",
    "    @birth.setter\n",
    "    def birth(self, value):\n",
    "        self._birth = value\n",
    "    \n",
    "    @property\n",
    "    def age(self):\n",
    "        return 2015 - self._birth\n",
    "        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "96c0fa7b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1990"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s = Student()\n",
    "s.birth = 1990\n",
    "s.birth"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "f0cf49c0",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "25"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s.age"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "68ea573c",
   "metadata": {},
   "source": [
    "注意，属性的方法名不要和实力变量名一致。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "9d0a1c18",
   "metadata": {},
   "outputs": [],
   "source": [
    "def birth(self):\n",
    "    return self.birth"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e2614e7b",
   "metadata": {},
   "source": [
    "会造成无线递归，最终报错"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "888bf141",
   "metadata": {},
   "source": [
    "### 小结"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d25d2d02",
   "metadata": {},
   "source": [
    "property广泛应用再类的定义中，可以让调用者写出简短的代码，同时保证对参数进行必要的检查。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "211974f5",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
